#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include<fcntl.h> 
#include <errno.h>
#include <time.h>

extern int errno; 

char TEST_FILE[] = "test.txt";

FILE *fp;

void open_file() {
	fp = fopen("ti.txt", "a");
}

void write_time(double t1, double t2) {
	fprintf(fp, "%f %f\n", t1, t2);
}

void close_file() {
	fclose(fp);
}

void test_syscall() {
	int rc;
	rc = syscall(SYS_chmod, TEST_FILE, 0664);
	if (rc == -1) {
		fprintf(stderr, "chmod failed, errno = %d\n", errno);
	} else {
		printf("System call SYS_chmod returned %d\n", rc);
	}

	long int amma = syscall(0);
	printf("System call sys_hello returned %ld\n", amma);
}

// int main() {
// 	int fd = open("test.txt", O_RDONLY);
// 	char buf2[32];
// 	int nbytes = sizeof(buf2);
// 	int br1 = read(fd, buf2, nbytes);
// 	int br2 = syscall(549, 30, fd, buf2, nbytes);
// 	printf("Read %d %d\n", br1, br2);
// }

char buf[32];
void ori_read() {
	/*
	(read, fd, buf, nbytes);
	*/
	int fd;
	fd = open(TEST_FILE, O_RDONLY);
	printf("ori_read fd = %d\n", fd); 

	int nbytes = sizeof(buf);
	int bytes_read = 1;
	int total = 0;
	while (bytes_read > 0) {
		bytes_read = read(fd, buf, nbytes);
		total += bytes_read;
	}
	printf("Read size %d\n", total);
	close(fd);
}

// system's origin system call
double my_read(const char *fi) {
	int fd = open(fi, O_RDONLY);
	// printf("my_read fd = %d\n", fd); 
	int nbytes = sizeof(buf);
	int bytes_read;
	int total = 0;
	clock_t start, end;
	double cpu_time_used;

	// micro benchmark
	start = clock();
	bytes_read = syscall(SYS_read, fd, buf, nbytes);
	end = clock();
	cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
	printf("Microbenchmark: read once Time: %fs\n", cpu_time_used);

	total = bytes_read;
	// macro benchmark
	start = clock();
	while (bytes_read > 0) {
		bytes_read = syscall(SYS_read, fd, buf, nbytes);
		total += bytes_read;
	}
	end = clock();
	cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
	printf("Marcobenchmark: read size %d Time: %fs\n", total, cpu_time_used);
	close(fd);
	return cpu_time_used;
}

// test my own system call
int SYS_myread = 549;
int client_id = 23;
double new_read(const char *fi) {
	int fd = open(fi, O_RDONLY);
	// printf("my_read fd = %d\n", fd); 
	int nbytes = sizeof(buf);
	int bytes_read;
	int total = 0;
	clock_t start, end;
	double cpu_time_used;

	// micro benchmark
	start = clock();
	bytes_read = syscall(SYS_myread, client_id, fd, buf, nbytes);
	end = clock();
	cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
	printf("Microbenchmark: read once Time: %fs\n", cpu_time_used);

	total = bytes_read;
	// macro benchmark
	start = clock();
	while (bytes_read > 0) {
		bytes_read = syscall(SYS_myread, client_id, fd, buf, nbytes);
		total += bytes_read;
	}
	end = clock();
	cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
	printf("Marcobenchmark: read size %d Time: %fs\n", total, cpu_time_used);
	close(fd);
	return cpu_time_used;
}

/*
1. create test file
dd if=/dev/zero of=test.txt bs=1 count=4MB
truncate -s 1g test2.txt
2. compare the file reading time of two kinds of read syscall
- The system's origin SYS_read
- Our own syscall SYS_myread
*/
int main(int argc, char *argv[])
{
	if (argc < 2) {
		printf("Usage: SyscallEva test.txt\n");
		return 0;
	}
	char *fi = argv[1];
	// test_syscall();
	// ori_read();

	open_file();
	double t1, t2;

	printf("Read file from path: %s\n", fi);
	printf("Origin Syscall-----------------------Start\n");
	t1 = my_read(fi);
	printf("Origin Syscall-----------------------End\n");


	printf("New Syscall-----------------------Start\n");
	t2 = new_read(fi);
	printf("New Syscall-----------------------End\n");

	write_time(t1, t2);

	close_file();

	printf("New Syscall2-----------------------Start\n");
	client_id = 105;
	new_read(fi);
	printf("New Syscall2-----------------------End\n");
	return 0;
}
